1 using UnityEngine;
2 using
System.Collections;
3
4 public
enum CharacterState
5 {
6     Idle =
0,
7     Walking =
1,
8     Trotting =
2,
9     Running =
3,
10     Jumping =
4,
11 }

12
13 public
class ThirdPersonController : MonoBehaviour
14 {
15
16     
public AnimationClip idleAnimation;
17     
public AnimationClip walkAnimation;
18     
public AnimationClip runAnimation;
19     
public AnimationClip jumpPoseAnimation;
20
21     
public float walkMaxAnimationSpeed = 0.75f;
22     
public float trotMaxAnimationSpeed = 1.0f;
23     
public float runMaxAnimationSpeed = 1.0f;
24     
public float jumpAnimationSpeed = 1.15f;
25     
public float landAnimationSpeed = 1.0f;
26
27     
private Animation _animation;
28
29   
30
31     
public CharacterState _characterState;
32
33     
// The speed when walking
34     
public float walkSpeed = 2.0f;
35     
// after trotAfterSeconds of walking we trot with trotSpeed
36     
public float trotSpeed = 4.0f;
37     
// when pressing "Fire3" button (cmd) we start running
38     
public float runSpeed = 6.0f;
39
40     
public float inAirControlAcceleration = 3.0f;
41
42     
// How high do we jump when pressing jump and letting go immediately
43     
public float jumpHeight = 0.5f;
44
45     
// The gravity for the character
46     
public float gravity = 20.0f;
47     
// The gravity in controlled descent mode
48     
public float speedSmoothing = 10.0f;
49     
public float rotateSpeed = 500.0f;
50     
public float trotAfterSeconds = 3.0f;
51
52     
public bool canJump = false;
53
54     
private float jumpRepeatTime = 0.05f;
55     
private float jumpTimeout = 0.15f;
56     
private float groundedTimeout = 0.25f;
57
58     
// The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
59     
private float lockCameraTimer = 0.0f;
60
61     
// The current move direction in x-z
62     
private Vector3 moveDirection = Vector3.zero;
63     
// The current vertical speed
64     
private float verticalSpeed = 0.0f;
65     
// The current x-z move speed
66     
private float moveSpeed = 0.0f;
67
68     
// The last collision flags returned from controller.Move
69     
private CollisionFlags collisionFlags;
70
71     
// Are we jumping? (Initiated with jump button and not grounded yet)
72     
private bool jumping = false;
73     
private bool jumpingReachedApex = false;
74
75     
// Are we moving backwards (This locks the camera to not do a 180 degree spin)
76     
private bool movingBack = false;
77     
// Is the user pressing any keys?
78     
private bool isMoving = false;
79     
// When did the user start walking (Used for going into trot after a while)
80     
private float walkTimeStart = 0.0f;
81     
// Last time the jump button was clicked down
82     
private float lastJumpButtonTime = -10.0f;
83     
// Last time we performed a jump
84     
private float lastJumpTime = -1.0f;
85     
// the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
86     
//private float lastJumpStartHeight = 0.0f;
87     
private Vector3 inAirVelocity = Vector3.zero;
88
89     
private float lastGroundedTime = 0.0f;
90     
public bool isControllable = true;
91
92     
void Awake()
93     {
94         moveDirection = transform.TransformDirection(Vector3.forward);
95
96         _animation = GetComponent<Animation>();
97         
if (!_animation)
98             Debug.Log(
"The character you would like to control doesn't have animations. Moving her might look weird.");
99
100         
/*
101     
public AnimationClip idleAnimation;
102     
public AnimationClip walkAnimation;
103     
public AnimationClip runAnimation;
104     
public AnimationClip jumpPoseAnimation;
105         */

106         
if (!idleAnimation)
107         {
108             _animation =
null;
109             Debug.Log(
"No idle animation found. Turning off animations.");
110         }
111         
if (!walkAnimation)
112         {
113             _animation =
null;
114             Debug.Log(
"No walk animation found. Turning off animations.");
115         }
116         
if (!runAnimation)
117         {
118             _animation =
null;
119             Debug.Log(
"No run animation found. Turning off animations.");
120         }
121         
if (!jumpPoseAnimation && canJump)
122         {
123             _animation =
null;
124             Debug.Log(
"No jump animation found and the character has canJump enabled. Turning off animations.");
125         }
126
127     }
128
129     
private Vector3 lastPos;
130
131     
void UpdateSmoothedMovementDirection()
132     {
133         Transform cameraTransform = Camera.main.transform;
134         
bool grounded = IsGrounded();
135
136         
// Forward vector relative to the camera along the x-z plane
137         Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
138         forward.y =
0;
139         forward = forward.normalized;
140
141         
// Right vector relative to the camera
142         
// Always orthogonal to the forward vector
143         Vector3 right =
new Vector3(forward.z, 0, -forward.x);
144
145         
float v = Input.GetAxisRaw("Vertical");
146         
float h = Input.GetAxisRaw("Horizontal");
147
148         
// Are we moving backwards or looking backwards
149         
if (v < -0.2f)
150             movingBack =
true;
151         
else
152             movingBack =
false;
153
154         
bool wasMoving = isMoving;
155         isMoving = Mathf.Abs(h) >
0.1f || Mathf.Abs(v) > 0.1f;
156
157         
// Target direction relative to the camera
158         Vector3 targetDirection = h * right + v * forward;
159
160         
// Grounded controls
161         
if (grounded)
162         {
163             
// Lock camera for short period when transitioning moving & standing still
164             lockCameraTimer += Time.deltaTime;
165             
if (isMoving != wasMoving)
166                 lockCameraTimer =
0.0f;
167
168             
// We store speed and direction seperately,
169             
// so that when the character stands still we still have a valid forward direction
170             
// moveDirection is always normalized, and we only update it if there is user input.
171             
if (targetDirection != Vector3.zero)
172             {
173                 
// If we are really slow, just snap to the target direction
174                 
if (moveSpeed < walkSpeed * 0.9f && grounded)
175                 {
176                     moveDirection = targetDirection.normalized;
177                 }
178                 
// Otherwise smoothly turn towards it
179                 
else
180                 {
181                     moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime,
1000);
182
183                     moveDirection = moveDirection.normalized;
184                 }
185             }
186
187             
// Smooth the speed based on the current target direction
188             
float curSmooth = speedSmoothing * Time.deltaTime;
189
190             
// Choose target speed
191             
//* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
192             
float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
193
194             _characterState = CharacterState.Idle;
195
196             // Pick speed modifier
197             
if (Input.GetKey(KeyCode.LeftShift) | Input.GetKey(KeyCode.RightShift))
198             {
199                 targetSpeed *= runSpeed;
200                 _characterState = CharacterState.Running;
201             }
202             
else if (Time.time - trotAfterSeconds > walkTimeStart)
203             {
204                 targetSpeed *= trotSpeed;
205                 _characterState = CharacterState.Trotting;
206             }
207             
else
208             {
209                 targetSpeed *= walkSpeed;
210                 _characterState = CharacterState.Walking;
211             }
212         
213             moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
214
215             // Reset walk time start
when we slow down
216             
if (moveSpeed < walkSpeed * 0.3f)
217                 walkTimeStart = Time.time;
218         }
219         // In air controls
220         
else
221         {
222             // Lock camera
while in air
223             
if (jumping)
224                 lockCameraTimer =
0.0f;
225
226             
if (isMoving)
227                 inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
228         }
229
230
231
232     }
233     
void ApplyJumping()
234     {
235         // Prevent jumping too fast after each other
236         
if (lastJumpTime + jumpRepeatTime > Time.time)
237             
return;
238
239         
if (IsGrounded())
240         {
241             // Jump
242             // - Only
when pressing the button down
243             // - With a timeout so you can press the button slightly before landing
244             
if (canJump && Time.time < lastJumpButtonTime + jumpTimeout)
245             {
246                 verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);
247                 SendMessage(
"DidJump", SendMessageOptions.DontRequireReceiver);
248             }
249         }
250     }
251     
void ApplyGravity()
252     {
253         
if (isControllable) // don't move player at all if not controllable.
254         {
255             // Apply gravity
256             //
bool jumpButton = Input.GetButton("Jump");
257             
258             // When we reach the apex of the jump we send
out a message
259             
if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)
260             {
261                 jumpingReachedApex =
true;
262                 SendMessage(
"DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
263             }
264
265             
if (IsGrounded())
266                 verticalSpeed =
0.0f;
267             
else
268                 verticalSpeed -= gravity * Time.deltaTime;
269         }
270     }
271
272     
float CalculateJumpVerticalSpeed(float targetJumpHeight)
273     {
274         // From the jump height and gravity we deduce the upwards speed
275         //
for the character to reach at the apex.
276         
return Mathf.Sqrt(2 * targetJumpHeight * gravity);
277     }
278
279     
void DidJump()
280     {
281         jumping =
true;
282         jumpingReachedApex =
false;
283         lastJumpTime = Time.time;
284         //lastJumpStartHeight = transform.position.y;
285         lastJumpButtonTime = -
10;
286
287         _characterState = CharacterState.Jumping;
288     }
289
290     Vector3 velocity = Vector3.zero;
291
292     
void Update()
293     {
294         
if (isControllable)
295         {
296             
if (Input.GetButtonDown("Jump"))
297             {
298                 lastJumpButtonTime = Time.time;
299             }
300
301             UpdateSmoothedMovementDirection();
302
303             // Apply gravity
304             // - extra power jump modifies gravity
305             // - controlledDescent mode modifies gravity
306             ApplyGravity();
307
308             // Apply jumping logic
309             ApplyJumping();
310
311
312             // Calculate actual motion
313             Vector3 movement = moveDirection * moveSpeed +
new Vector3(0, verticalSpeed, 0) + inAirVelocity;
314             movement *= Time.deltaTime;
315
316             // Move the controller
317             CharacterController controller = GetComponent<CharacterController>();
318             collisionFlags = controller.Move(movement);
319         }
320         velocity = (transform.position - lastPos)*
25;
321
322         // ANIMATION sector
323         
if (_animation)
324         {
325             
if (_characterState == CharacterState.Jumping)
326             {
327                 
if (!jumpingReachedApex)
328                 {
329                     _animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
330                     _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
331                     _animation.CrossFade(jumpPoseAnimation.name);
332                 }
333                 
else
334                 {
335                     _animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
336                     _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
337                     _animation.CrossFade(jumpPoseAnimation.name);
338                 }
339             }
340             
else
341             {
342                 
if (this.isControllable && velocity.sqrMagnitude < 0.001f)
343                 {
344                     _characterState = CharacterState.Idle;
345                     _animation.CrossFade(idleAnimation.name);
346                 }
347                 
else
348                 {
349                     
if (_characterState == CharacterState.Idle)
350                     {
351                         _animation.CrossFade(idleAnimation.name);
352                     }
353                     
else if (_characterState == CharacterState.Running)
354                     {
355                         _animation[runAnimation.name].speed = runMaxAnimationSpeed;
356                         
if (this.isControllable)
357                         {
358                             _animation[runAnimation.name].speed = Mathf.Clamp(velocity.magnitude,
0.0f, runMaxAnimationSpeed);
359                         }
360                         _animation.CrossFade(runAnimation.name);
361                     }
362                     
else if (_characterState == CharacterState.Trotting)
363                     {
364                         _animation[walkAnimation.name].speed = trotMaxAnimationSpeed;
365                         
if (this.isControllable)
366                         {
367                             _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude,
0.0f, trotMaxAnimationSpeed);
368                         }
369                         _animation.CrossFade(walkAnimation.name);
370                     }
371                     
else if (_characterState == CharacterState.Walking)
372                     {
373                         _animation[walkAnimation.name].speed = walkMaxAnimationSpeed;
374                         
if (this.isControllable)
375                         {
376                             _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude,
0.0f, walkMaxAnimationSpeed);
377                         }
378                         _animation.CrossFade(walkAnimation.name);
379                     }
380
381                 }
382             }
383         }
384         // ANIMATION sector
385
386         // Set rotation to the move direction
387         
if (IsGrounded())
388         {
389
390             transform.rotation = Quaternion.LookRotation(moveDirection);
391
392         }
393         
else
394         {
395             /* This causes choppy behaviour
when colliding with SIDES
396              * Vector3 xzMove = velocity;
397             xzMove.y =
0;
398             
if (xzMove.sqrMagnitude > 0.001f)
399             {
400                 transform.rotation = Quaternion.LookRotation(xzMove);
401             }*/

402         }
403
404         
// We are in jump mode but just became grounded
405         
if (IsGrounded())
406         {
407             lastGroundedTime = Time.time;
408             inAirVelocity = Vector3.zero;
409             
if (jumping)
410             {
411                 jumping =
false;
412                 SendMessage(
"DidLand", SendMessageOptions.DontRequireReceiver);
413             }
414         }
415
416         lastPos = transform.position;
417     }
418
419     
void OnControllerColliderHit(ControllerColliderHit hit)
420     {
421         
// Debug.DrawRay(hit.point, hit.normal);
422         
if (hit.moveDirection.y > 0.01f)
423             
return;
424     }
425
426     
public float GetSpeed()
427     {
428         
return moveSpeed;
429     }
430
431     
public bool IsJumping()
432     {
433         
return jumping;
434     }
435
436     
public bool IsGrounded()
437     {
438         
return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
439     }
440
441     
public Vector3 GetDirection()
442     {
443         
return moveDirection;
444     }
445
446     
public bool IsMovingBackwards()
447     {
448         
return movingBack;
449     }
450
451     
public float GetLockCameraTimer()
452     {
453         
return lockCameraTimer;
454     }
455
456     
public bool IsMoving()
457     {
458         
return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
459     }
460
461     
public bool HasJumpReachedApex()
462     {
463         
return jumpingReachedApex;
464     }
465
466     
public bool IsGroundedWithTimeout()
467     {
468         
return lastGroundedTime + groundedTimeout > Time.time;
469     }
470
471     
public void Reset()
472     {
473         gameObject.tag =
"Player";
474     }
475
476
477 }


The speed when walking

after trotAfterSeconds of walking we trot with trotSpeed

when pressing "Fire3" button (cmd) we start running

How high do we jump when pressing jump and letting go immediately

The gravity for the character

The gravity in controlled descent mode

The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.

The current move direction in x-z

The current vertical speed

The current x-z move speed

The last collision flags returned from controller.Move

Are we jumping? (Initiated with jump button and not grounded yet)

Are we moving backwards (This locks the camera to not do a 180 degree spin)

Is the user pressing any keys?

When did the user start walking (Used for going into trot after a while)

Last time the jump button was clicked down

Last time we performed a jump

the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)

private float lastJumpStartHeight = 0.0f;

Forward vector relative to the camera along the x-z plane

Right vector relative to the camera

Always orthogonal to the forward vector

Are we moving backwards or looking backwards

Target direction relative to the camera

Grounded controls

Lock camera for short period when transitioning moving & standing still

We store speed and direction seperately,

so that when the character stands still we still have a valid forward direction

moveDirection is always normalized, and we only update it if there is user input.

If we are really slow, just snap to the target direction

Otherwise smoothly turn towards it

Smooth the speed based on the current target direction

Choose target speed

* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways

Pick speed modifier

Reset walk time start when we slow down

In air controls

Lock camera while in air

Prevent jumping too fast after each other

Jump

- Only when pressing the button down

- With a timeout so you can press the button slightly before landing

if (isControllable) don't move player at all if not controllable.

Apply gravity

bool jumpButton = Input.GetButton("Jump");

When we reach the apex of the jump we send out a message

From the jump height and gravity we deduce the upwards speed

for the character to reach at the apex.

lastJumpStartHeight = transform.position.y;

Apply gravity

- extra power jump modifies gravity

- controlledDescent mode modifies gravity

Apply jumping logic

Calculate actual motion

Move the controller

ANIMATION sector

ANIMATION sector

Set rotation to the move direction

We are in jump mode but just became grounded

Debug.DrawRay(hit.point, hit.normal);




Trò chơi Tic-Tac-Toe, game đánh caro full source code 53.536 lượt xem

Gõ tìm kiếm nhanh...